﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ICommonLibrary.Collection {

    public class CollectionUtil {

        /// <summary>
        /// Compares two lists. Null elements are considered to be equal.
        /// </summary>
        /// <param name="list1"></param>
        /// <param name="list2"></param>
        /// <returns>a negative integer, zero, or a positive integer as the first
        /// argument is less than, equal to, or greater than the second.</returns>
        public static int Compares(IList<IComparable> list1, IList<IComparable> list2) {
		    if (list1 == null && list2 == null)
			    return 0;
		    if (list1 == null)
			    return -1;
		    if (list2 == null)
			    return 1;
		    int l = list1.Count;
            int r = list2.Count;
		    if (l != r)
			    return l - r;
		    for (int i = 0; i < l; i++) {
			    int compareTo = Util.Compares(list1[i], list2[i]);
			    if (compareTo != 0)
				    return compareTo;
		    }
		    return 0;
	    }

        public new static bool Equals(object o1, object o2) {
            throw new InvalidOperationException("You may want to call Equals(ICollection, ICollection)");
        }

        /// <summary>
        /// Test the equality of two ordered collections. Null elements are considered to be
        /// equal.
        /// </summary>
        /// <param name="d1"></param>
        /// <param name="d2"></param>
        /// <returns>true if they are equal, false otherwise</returns>
        public static bool Equals<TK,TV>(Dictionary<TK,TV> d1, Dictionary<TK,TV> d2) {
            if (d1 == null && d2 == null)
                return true;
            if (d1 == null||d2 == null)
                return false;

            int l = d1.Count;
            int r = d2.Count;
            if (l != r)
                return false;

            Dictionary<TK, TV>.Enumerator i = d1.GetEnumerator();
            while (i.MoveNext()) {
                KeyValuePair<TK, TV> e = i.Current;
                TK key = e.Key;
                TV value = e.Value;

                if (ReferenceEquals(value, null)) {
                    TV v2;
                    if (d2.TryGetValue(key, out v2)) {
                        if (v2 != null) {
                            return false;
                        }
                    }
                    else {
                        return false;
                    }
                }
                else {
                    TV v2;
                    if (d2.TryGetValue(key, out v2)) {
                        if (!value.Equals(v2)) {
                            return false;
                        }
                    }
                    else {
                        return false;
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// Test the equality of two ordered collections. Null elements are considered to be
        /// equal.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="c1"></param>
        /// <param name="c2"></param>
        /// <returns>true if they are equal, false otherwise</returns>
        public static bool Equals<T>(ICollection<T> c1, ICollection<T> c2) {
            if (c1 == null && c2 == null)
                return true;
            if (c1 == null||c2 == null)
                return false;
            int l = c1.Count;
            int r = c2.Count;
            if (l != r)
                return false;

            IEnumerator<T> e1 = c1.GetEnumerator();
            IEnumerator<T> e2 = c2.GetEnumerator();
            while(e1.MoveNext() && e2.MoveNext()) {
                if (!Util.Equals(e1.Current, e2.Current)) {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Returns true if the supplied Collection is empty, false otherwise.
        /// </summary>
        /// <param name="collection"></param>
        /// <returns></returns>
	    public static bool IsEmpty<T>(ICollection<T> collection) {
		    return (collection == null || collection.Count == 0);
	    }

        /**
         * Build a string by concatnating all elements in a collection with
         * specified string as glue. Any element having <code>null</code> value
         * is ignored.
         * 
         * <p>
         * For example:
         * </p>
         * 
         * <pre>
         * <li>
         * Collection a = Arrays.asList(new String[] { &quot;A&quot;, &quot;B&quot;, &quot;C&quot; });
         * System.out.println(join(&quot;|&quot;, a)); // A|B|C
         * </li>
         * <li>
         * Collection a = Arrays.asList(new String[] { &quot;A&quot;, null, &quot;C&quot; });
         * System.out.println(join(&quot;|&quot;, a)); // A|C
         * </li>
         * <li>
         * Collection a = Arrays.asList(new String[] {});
         * System.out.println(join(&quot;|&quot;, a)); // (empty string)
         * </li>
         * <li>
         * Collection a = Arrays.asList(new String[] { &quot;A&quot;, null, &quot;C&quot; });
         * System.out.println(join(null, a)); // AC
         * </li>
         * </pre>
         * 
         * @param glue -
         *            the String to join the array, may be null.
         * @return join result.
         */
        public static string Join<T>(String glue, ICollection<T> c) {
            return Join(glue, c, false);
        }

        /// <summary>
        /// Build a string by concatnating every element of a collection with
        /// the specified string as glue.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="glue"></param>
        /// <param name="c"></param>
        /// <param name="ignoreNullOrEmpty"></param>
        /// <returns></returns>
        public static string Join<T>(string glue, ICollection<T> c, bool ignoreNullOrEmpty) {

		    if (c == null)
			    throw new NullReferenceException();
    	
		    if (c.Count == 0) {
			    return string.Empty;
		    }
    		
		    IEnumerator<T> iter = c.GetEnumerator();
	        iter.MoveNext();
    		
		    var buf = new StringBuilder(100);
		    buf.Append(Util.GetString(iter.Current)); // call Util.GetString() to prevent null.
    		
		    while(iter.MoveNext()) {
			    string next = Util.GetString(iter.Current, null);
                if (string.IsNullOrEmpty(next)) {
                    if (ignoreNullOrEmpty) {
                        continue;
                    }
                    next = string.Empty;
                }
			    if (glue != null)
				    buf.Append(glue);
			    buf.Append(next);
		    }
    		
		    return buf.ToString();
	    }

        public static string Join<T>(string glue, ICollection<T> c, Func<T, string> converter) {

            if (c == null)
                throw new NullReferenceException();

            if (c.Count == 0) {
                return string.Empty;
            }

            IEnumerator<T> iter = c.GetEnumerator();
            iter.MoveNext();

            var buf = new StringBuilder(c.Count * 30);
            buf.Append(Util.GetString(converter(iter.Current))); // call Util.GetString() to prevent null.

            while (iter.MoveNext()) {
                string next = Util.GetString(converter(iter.Current));
                if (glue != null)
                    buf.Append(glue);
                buf.Append(next);
            }
            return buf.ToString();
        }

        /// <summary>
        /// Converts a collection to an array.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="c"></param>
        /// <returns></returns>
	    public static T[] ToArray<T>(ICollection<T> c) {
		    Asserts.NotNull(c);
    		T[] a = new T[c.Count];
            c.CopyTo(a, 0);
		    return a;
	    }

        public static List<T> ToList<T>(params T[] items) {
            return new List<T>(items);
        }

        public static List<T> ToList<T>(IEnumerator<T> enumerator) {
            var list = new List<T>(100);
            while (enumerator.MoveNext()) {
                list.Add(enumerator.Current);
            }
            return list;
        }
    	
    }
}
